package com.hzw.saas.service.property.config;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.springframework.boot.context.event.ApplicationContextInitializedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import lombok.extern.slf4j.Slf4j;

/**
 * 从数据库读取参数至spring context environment
 * 默认在bootstrap配置读取完成之后执行（需要从远程配置文件中获取数据库连接信息）
 *
 * @author zzl
 * @since 09/15/2021
 */
@Slf4j
public class DbPropertyLoader implements ApplicationListener<ApplicationContextInitializedEvent>, Ordered {

    private static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";

    private static final String DB_PROPERTY_SOURCE_NAME = "database-config";
    private static final String KEY_FORMAT = "%s.%s";

    private static final String PARAM_SQL = "SELECT `module`, `key`, `value` FROM tb_sys_param WHERE status = 1";
    private static final String DICT_SQL = "SELECT `module`, `key`, `value` FROM tb_sys_dict WHERE status = 1";
    private static final String MODULE_COLUMN = "module";
    private static final String KEY_COLUMN = "key";
    private static final String VALUE_COLUMN = "value";

    public DbPropertyLoader() {
        log.debug("DbPropertyLoader loaded...");
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }

    @Override
    public void onApplicationEvent(ApplicationContextInitializedEvent event) {
        ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
        // don't listen to events in a bootstrap context
        if (environment.getPropertySources().contains(BOOTSTRAP_PROPERTY_SOURCE_NAME)) {
            return;
        }
        log.debug("load properties form database...");
        String url = environment.getRequiredProperty("spring.datasource.url");
        String username = environment.getRequiredProperty("spring.datasource.username");
        String password = environment.getRequiredProperty("spring.datasource.password");
        String driverName = environment.getRequiredProperty("spring.datasource.driver-class-name");
        log.debug("database url: {}", url);
        log.debug("database username: {}", username);
        log.debug("database driverName: {}", driverName);
        try {
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("DbPropertyLoader load failed", e);
        }
        try (Connection conn = DriverManager.getConnection(url, username, password);
             Statement stmt = conn.createStatement();) {
            Map<String, String> dbPropertyMap = new HashMap<>();
            log.debug("query: {}", PARAM_SQL);
            ResultSet rs1 = stmt.executeQuery(PARAM_SQL);
            this.setProperty(dbPropertyMap, rs1);
            log.debug("query: {}", DICT_SQL);
            ResultSet rs2 = stmt.executeQuery(DICT_SQL);
            this.setProperty(dbPropertyMap, rs2);
            log.debug("total: {}", dbPropertyMap.size());
            // 转换参数为属性
            Properties dbProperties = new Properties();
            dbProperties.putAll(dbPropertyMap);
            // 转换属性为集合
            PropertiesPropertySource dbPropertySource = new PropertiesPropertySource(DB_PROPERTY_SOURCE_NAME, dbProperties);
            // 获取系统属性集合
            MutablePropertySources propertySources = environment.getPropertySources();
            // 添加到系统变量
            propertySources.addFirst(dbPropertySource);
            log.debug("load properties form database success.");
        } catch (SQLException e) {
            throw new RuntimeException("DbPropertyLoader load failed", e);
        }
    }

    private void setProperty(Map<String, String> propertyMap, ResultSet rs) throws SQLException {
        while (rs.next()) {
            String key = String.format(KEY_FORMAT, rs.getString(MODULE_COLUMN), rs.getString(KEY_COLUMN));
            String value = rs.getString(VALUE_COLUMN);
            propertyMap.put(key, value);
        }
    }


}
